﻿using System;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using élénkPróbája.extentions;
using élénkPróbája.Test.dto;
using élénkPróbája.Test.extention;
using élénkPróbája.validators;

namespace élénkPróbája.Test
{
    [TestClass]
    public class UnequalValidatorTester
    {
        public UnequalValidatorTester()
        {
            CultureScope.SetDefaultCulture();
        }
         
        [TestMethod]
        public void When_the_objects_are_equal_then_the_validator_should_fail()
        {
            var validator = new TestValidator(v => v.RuleFor(x => x.Name).NotEqual("Foo"));
            var result = validator.Validate(new Company { Name = "Foo" });
            result.IsValid.ShouldBeFalse();
        }

        [TestMethod]
        public void When_the_objects_are_not_equal_then_the_validator_should_pass()
        {
            var validator = new TestValidator(v => v.RuleFor(x => x.Name).NotEqual("Bar"));
            var result = validator.Validate(new Company { Name = "Foo" });
            result.IsValid.ShouldBeTrue();
        }

        [TestMethod]
        public void When_the_validator_fails_the_error_message_should_be_set()
        {
            var validator = new TestValidator(v => v.RuleFor(x => x.Name).NotEqual("Foo"));
            var result = validator.Validate(new Company { Name = "Foo" });
            result.Errors.Single().ErrorMessage.ShouldEqual("'' should not be equal to 'Foo'.");
        }

        [TestMethod]
        public void Validates_across_properties()
        {
            var validator = new TestValidator(
                v => v.RuleFor(x => x.Name).NotEqual(x => x.PublicName)
            );

            var result = validator.Validate(new Company { PublicName = "foo", Name = "foo" });
            result.IsValid.ShouldBeFalse();
        }


        [TestMethod]
        public void Should_store_property_to_compare()
        {
            var validator = new TestValidator(v => v.RuleFor(x => x.Name).NotEqual(x => x.PublicName));
            var propertyValidator = validator.CreateDescriptor()
                .GetValidatorsForMember("Name")
                .OfType<UnequalValidator>()
                .Single();

            propertyValidator.MemberToCompare.ShouldEqual(typeof(Company).GetProperty("PublicName"));
        }

        [TestMethod]
        public void Should_store_comparison_type()
        {
            var validator = new TestValidator(v => v.RuleFor(x => x.Name).NotEqual(x => x.PublicName));
            var propertyValidator = validator.CreateDescriptor()
                .GetValidatorsForMember("Name")
                .OfType<UnequalValidator>()
                .Single();
            propertyValidator.Comparison.ShouldEqual(Comparison.NotEqual);
        }

        [TestMethod]
        public void Should_not_be_valid_for_case_insensitve_comparison()
        {
            var validator = new TestValidator(v => v.RuleFor(x => x.Name).NotEqual("FOO", StringComparer.OrdinalIgnoreCase));
            var result = validator.Validate(new Company { Name = "foo" });
            result.IsValid.ShouldBeFalse();
        }

        [TestMethod]
        public void Should_not_be_valid_for_case_insensitve_comparison_with_expression()
        {
            var validator = new TestValidator(v => v.RuleFor(x => x.Name).NotEqual(x => x.PublicName, StringComparer.OrdinalIgnoreCase));
            var result = validator.Validate(new Company { Name = "foo", PublicName = "FOO" });
            result.IsValid.ShouldBeFalse();
        }

        [TestMethod]
        public void Should_handle_custom_value_types_correctly()
        {
            var myType = new MyType();
            var myTypeValidator = new MyTypeValidator();

            var validationResult = myTypeValidator.Validate(myType);
            validationResult.IsValid.ShouldEqual(false);
        }

        public class MyType
        {
            public MyValueType Value { get; set; }
        }

        public class MyTypeValidator : AbstractValidator<MyType>
        {
            public MyTypeValidator()
            {
                RuleFor(myType => myType.Value).NotEqual(MyValueType.None);
            }
        }

        public struct MyValueType
        {
            public static readonly MyValueType None = default(MyValueType);

            public MyValueType(int value)
            {
                _value = value;
            }

            public int Value
            {
                get { return _value ?? -1; }
            }

            private readonly int? _value;

            public override int GetHashCode()
            {
                return _value == null ? 0 : _value.Value.GetHashCode();
            }

            public override string ToString()
            {
                return _value == null ? null : _value.Value.ToString();
            }

            public override bool Equals(object obj)
            {
                if (obj == null || obj.GetType() != typeof(MyValueType))
                    return false;

                var otherValueType = (MyValueType)obj;
                return Equals(otherValueType);
            }

            public bool Equals(MyValueType other)
            {
                return _value == other._value;
            }

            public static bool operator ==(MyValueType first, MyValueType second)
            {
                return first.Equals(second);
            }

            public static bool operator !=(MyValueType first, MyValueType second)
            {
                return !(first == second);
            }
        }
    }
}
